(* file: lexer.mll *)
{
	open Grammar					(* the parser file is "grammar.mly" *)
	exception Eof

	let lineno = ref 1 ;;			(* count line number *)
	let count_nl s = 
		let rec count_int s size = 
			begin match size with
			  -1 ->	()
			| x -> 	if (String.get s x == '\n')
					then begin
						incr lineno ;
						count_int s (x-1)
					end else
						count_int s (x-1) 
			end
		in (count_int s (String.length(s)-1))
	;;
}

let digit = ['0'-'9']
let lowercase = ['a'-'z']
let uppercase = ['A'-'Z']

let skip_nl = [' ' '\t' '\n']*


rule token = parse
			(*** Separateurs ***)
	| [' ' '\t']								{ token lexbuf }
	| '\n'skip_nl		 						{ count_nl (Lexing.lexeme lexbuf) ; NEWLINE }
	| ','										{ COMMA }
			(*** Types ***)
	| digit+ as num								{ INTEGER (int_of_string num) }
	| '-'digit+ as num							{ INTEGER (int_of_string num) }

	| digit+'.'digit+ as num					{ FLOAT (float_of_string num) }
	| '-'digit+'.'digit+ as num					{ FLOAT (float_of_string num) }

	| "true"|"false"							{ BOOLEAN (bool_of_string(Lexing.lexeme lexbuf)) }
	| '"'										{ parse_string "" lexbuf }

			(*** Operateurs et comparateurs arithmétiques ***)
	| ['+' '-' '*' '/' '<' '>']					{ OPERATOR_BIN (Lexing.lexeme lexbuf) }
	| "=="|"!="|"<="|">="						{ OPERATOR_BIN (Lexing.lexeme lexbuf) }
	| "**"										{ OPERATOR_BIN (Lexing.lexeme lexbuf) }
	| "&&"|"||"									{ OPERATOR_BIN (Lexing.lexeme lexbuf) }
	| "not"|"!"									{ OPERATOR_UNARY (Lexing.lexeme lexbuf) }
	| '='				     					{ EQUAL }
			(*** Brackets ***)
	| '('										{ LPAREN }
	| ')'										{ RPAREN }
	| skip_nl '{' skip_nl						{ count_nl (Lexing.lexeme lexbuf) ; LBRACE } (* skip newline *)
	| skip_nl '}'		 						{ count_nl (Lexing.lexeme lexbuf) ; RBRACE } (* skip newline *)

	| '['										{ LBRACKET }
	| ']'										{ RBRACKET }
			(*** Structures de controles ***)
	| "if" 										{ IF }
	| skip_nl "elsif"							{ count_nl (Lexing.lexeme lexbuf) ; ELSIF }
	| "else"									{ ELSE }
	| "for"										{ FOR }
	| "while"									{ WHILE }
	| "end"										{ END }
	| "in"										{ IN }
	| "do" 										{ DO } 
	| '.'										{ DOT }
			(*** Définition de fonctions ***)
	| "def"										{ DEF }
	| "return"									{ RETURN }
			(*** Fonctions d'affichage ***)
	| "print" | "puts"							{ PRINT }
			(*** Variable ***)
	| ('_'|lowercase+)(digit|lowercase|uppercase|'_')*
                        { VAR (Lexing.lexeme lexbuf) }
	| uppercase+								{ VAR (Lexing.lexeme lexbuf) }
  			(*** Comments ***) 
	| "=begin"									{ multi_line_comment 1 lexbuf }
	| "=end"									{ failwith "too many end comment" }
	| '#' 										{ one_line_comment lexbuf }
			(*** End of File et mots non reconnus ***)
	| eof				   						{ raise Eof }
	| _											{ failwith 
                            ("Bad token on line "^string_of_int !lineno) }

(* machine to parse a string *)
and parse_string str = parse
	 '"'										{ STRING(str) }
	| _ as c									{ parse_string (str^(String.make 1 c)) lexbuf }
(* machine to parse one-line comments *)
and one_line_comment = parse
	 '\n'										{ incr lineno ; token lexbuf }
	| _ 										{ one_line_comment lexbuf }

(* machine to parse multi-line nested comments *)
and multi_line_comment deep = parse
	 "=end"				{ if ( deep == 1 ) then token lexbuf else multi_line_comment 
                                                           (deep-1) lexbuf }
	| "=begin"									{ multi_line_comment (deep+1) lexbuf }
	| '\n'										{ incr lineno ; multi_line_comment deep lexbuf }
	| eof										{ failwith ("too many begin comment") }
	| _ 										{ multi_line_comment deep lexbuf }